আমার আগের পোস্টে আমি পান্ডাসের বেসিক পরিচিতি এবং এর একমাত্রিক ডাটা স্ট্রাকচার সিরিজ নিয়ে কথা বলেছি। এখন আমি মাত্রা বাড়িয়ে দিব ও আমাদের এখনকার টপিক হবে ডাটাফ্রেম।

ডাটাফ্রেম হল পান্ডাসের দ্বিমাত্রিক ডাটা স্ট্রাকচার। রো ও কলাম দুই বরাবর আপনি ইনডেক্স করতে পারবেন আর সর্টিং, সারচিং, গ্রুপিং ইত্যাদি করা যাবে। প্লটিং তো আছেই। বলতে পারেন, পাইথনের ভিতর এক্সেল টাইপ কাজ করতে পারবেন, প্রোগ্রামেটিকালী। (অনেকে বলে এটি R এর data.frame এর মত কিন্তু আমি R পারি না তো তা বলতে পারছি না)।


In [1]:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

In [15]:
# দ্বিমাত্রিক অ্যারে (অথবা লিস্টের ভিতর লিস্ট) দিয়ে আমরা ডাটাফ্রেম ইনিসিয়ালাইজ করতে পারি। 
fake_2d_data = np.random.randn(25).reshape(5, 5)
df = pd.DataFrame(fake_2d_data, 
                  index=["alpha", "beta", "gamma", "delta", "epsilon"],
                  columns=["A", "B", "C", "D", "E"])
df.plot(kind="barh", stacked=True)


Out[15]:
<matplotlib.axes.AxesSubplot at 0xb9c0b6c>

আমরা উপরের ডাটাফ্রেমের অক্ষ ঘুরাতে পারি ট্রান্সপোজের মাধ্যমে।


In [16]:
df_t = df.T
df_t.plot(kind='barh', stacked=True)


Out[16]:
<matplotlib.axes.AxesSubplot at 0xb9f70ac>

যখন আইপাইথন নোটবুকে কাজ করা হবে তখন নিচের কোড দিয়ে সুন্দরভাবে এইচটিএমএল টেব্ল প্রিন্ট করা যায়।


In [18]:
from IPython.core.display import HTML
display(HTML(df.to_html()))


A B C D E
alpha 0.960272 -0.826986 -0.466003 -2.164979 -0.515156
beta -0.552988 -0.377938 -0.221681 -0.198346 0.023387
gamma 1.961069 1.710462 -0.438632 1.606601 -1.558174
delta -0.674267 0.091664 0.262994 -1.355863 -1.075137
epsilon -1.406780 0.781694 -0.257940 -0.012960 0.142525

In [19]:
display(HTML(df_t.to_html()))


alpha beta gamma delta epsilon
A 0.960272 -0.552988 1.961069 -0.674267 -1.406780
B -0.826986 -0.377938 1.710462 0.091664 0.781694
C -0.466003 -0.221681 -0.438632 0.262994 -0.257940
D -2.164979 -0.198346 1.606601 -1.355863 -0.012960
E -0.515156 0.023387 -1.558174 -1.075137 0.142525

JSON, CSV, HTML ইত্যাদি ফরম্যাটও রয়েছে। নিচের কোড থেকে দেখা যাবে।


In [30]:
print filter(lambda i: i.startswith("to_"), dir(df))


['to_clipboard', 'to_csv', 'to_dense', 'to_dict', 'to_excel', 'to_gbq', 'to_hdf', 'to_html', 'to_json', 'to_latex', 'to_msgpack', 'to_panel', 'to_period', 'to_pickle', 'to_records', 'to_sparse', 'to_sql', 'to_stata', 'to_string', 'to_timestamp', 'to_wide']

আমরা আমাদের ডাটা সম্বন্ধিত পরিসংখ্যান পেতে পারি ডিস্ক্রাইব ফাংশনের মাধ্যমে।


In [32]:
df.describe()


Out[32]:
A B C D E
count 5.000000 5.000000 5.000000 5.000000 5.000000
mean 0.057461 0.275779 -0.224253 -0.425109 -0.596511
std 1.368472 0.998950 0.292811 1.435495 0.723005
min -1.406780 -0.826986 -0.466003 -2.164979 -1.558174
25% -0.674267 -0.377938 -0.438632 -1.355863 -1.075137
50% -0.552988 0.091664 -0.257940 -0.198346 -0.515156
75% 0.960272 0.781694 -0.221681 -0.012960 0.023387
max 1.961069 1.710462 0.262994 1.606601 0.142525

একটু নন-র‍্যান্ডম ডাটা নিয়ে কাজ করা যাক যাতে আমাদের কাজগুলো ভেরিফাইড হয়।


In [33]:
df = pd.DataFrame(np.arange(16).reshape(4, 4),
                  index=list("ABCD"),
                  columns=list("WXYZ"))

display(HTML(df.to_html()))


W X Y Z
A 0 1 2 3
B 4 5 6 7
C 8 9 10 11
D 12 13 14 15

এবার পালা কিছু অপারেশানের। মনে রাখবেন রো হল ১, আর কলাম হল ০।


In [54]:
total_by_columns = df.sum(axis=0)
total_by_rows = df.sum(axis=1)

cumsum_by_columns = df.cumsum(axis=0)
cumsum_by_rows = df.cumsum(axis=1)

percentage_change_by_columns = df.pct_change(axis=0)
percentage_change_by_row = df.pct_change(axis=1)

# See also: mean, median, mad, var, skew, curt etc. Use ipython's ? and ?? for help.

ডাটা কালেকশনের সময়ে প্রায়েই আপনি মিসিং ডাটা পাবেন। যা কিনা আপনার কাঠামোতে গ্যাপ হিসাবে থাকবে। এগুলো হ্যান্ডল করার টুল দিয়েছে আপনাকে পান্ডাস। যার বেশিরভাগই "na" (অর্থাৎ not available) দিয়ে শেষ হয়। উদাহরণস্বরূপ দেখি-


In [83]:
print filter(lambda i: i.endswith("na") or "null" in i, dir(df))


['dropna', 'fillna', 'isnull', 'notnull']

In [70]:
pd.DataFrame([[1, None, 2, 4, None], [2, 3, 3, 5]]).dropna()


Out[70]:
0 1 2 3 4

In [72]:
pd.DataFrame([[1, None, 2, 4, None], [2, 3, 3, 5]]).fillna("শূন্য")


Out[72]:
0 1 2 3 4
0 1 শূন্য 2 4 শূন্য
1 2 3 3 5 শূন্য

আপনি যেই অক্ষই চিন্তা করেননা কেন, আপনি এক ধাপ ধরলে একটি অ্যারে পাবেন। যদি অক্ষ হয় ০ তাহলে কলাম, অন্যথায় রো। এখন, আপনি sum, cumsum, mean, median, var ইত্যাদি পাচ্ছেন সংশ্লিষ্ট অপারেশানের জন্য। কিন্তু আপনি যদি নিজের কিছু চান তাহলে? তাহলে আপনার এমন ফাংশন লিখতে হবে যার প্রথম প্যারামিটার অ্যারে, এবং আপনি আপনার ডাটাফ্রামের সাথে তাকে আপ্লাই করবেন। যেমন নিচের কোডটিকেই ধরুন-


In [73]:
df = pd.DataFrame(np.arange(10).reshape(5, 2))
df


Out[73]:
0 1
0 0 1
1 2 3
2 4 5
3 6 7
4 8 9

In [82]:
def multiply_with(df, n):
    return df * n

df.apply(multiply_with, n=10)


Out[82]:
0 1
0 0 10
1 20 30
2 40 50
3 60 70
4 80 90

In [85]:
df = pd.DataFrame(np.random.randn(3, 5))
df


Out[85]:
0 1 2 3 4
0 0.080426 2.012099 0.919150 -0.379672 -1.204189
1 0.231752 0.451970 -1.732385 0.702375 -0.860986
2 -0.551936 0.911473 -1.215514 -0.694774 1.312840

এইবার ইন্ডেক্সিং। বিশেষ এক ডাটাফ্রেম তৈরি করি যেন ইন্ডেক্সিং বুঝতে সুবিধা হয়।


In [105]:
df = pd.DataFrame([["00", "01", "02", "03"],
                   ["10", "11", "12", "13"],
                   ["20", "21", "22", "23"],
                   ["40", "41", "42", "43"],],
                  index=["r0", "r1", "r2", "r3"],
                  columns=["c0", "c1", "c2", "c3"])
df


Out[105]:
c0 c1 c2 c3
r0 00 01 02 03
r1 10 11 12 13
r2 20 21 22 23
r3 40 41 42 43

ডাটাফ্রেম আর নামপাই অ্যারে একই রকম ইন্ডেক্সিং ব্যবহার করে এক বিশেষ ফাংশন ix এর মাধ্যমে।


In [108]:
df.ix[2, :] # রো ২ এ স্থির।


Out[108]:
c0    20
c1    21
c2    22
c3    23
Name: r2, dtype: object

In [109]:
df.ix[:, 3] # কলাম ৩ এ স্থির।


Out[109]:
r0    03
r1    13
r2    23
r3    43
Name: c3, dtype: object

In [111]:
df.ix[1:2, 1:3] # ১ থেকে ২ এর আগ পর্যন্ত রো, ১ থেকে ৩ এর আগ পর্যন্ত কলাম।


Out[111]:
c1 c2
r1 11 12

কিন্তু ix কেন? সরাসরি [] কেন ইউজ করলাম না? কারণ, [] দিয়ে কলাম বের করব। আর প্রতিটি কলাম হল ডাটাফ্রেমের অ্যাট্রিবিউট। আর তা আপনাকে দিবে একটি সিরিজ যার অ্যাট্রিবিউট হবে রো।


In [119]:
df["c1"] # df.c1 দিলেও চলত। জাভাস্ক্রিপ্টের মত।


Out[119]:
r0    01
r1    11
r2    21
r3    41
Name: c1, dtype: object

In [120]:
type(df.c1)


Out[120]:
pandas.core.series.Series

In [121]:
df.c1.r1


Out[121]:
'11'

এতক্ষণ দেখলাম যে ২-ডি অ্যারে দিয়েই ডাটাফ্রেম তৈরি করেছি। কিন্তু আসলে আরেকভাবে ডাটাফ্রেম তৈরি করা যায়। ডিকশনারি দিয়ে। এটি zip নিয়ম ফলো করে নামপাইএর মত।


In [160]:
data = {
    "names": ["Uruguay", "Brazil", "Argentina", "Germany", "Italy", "Spain", "England"],
    "participated": [12, 20, 16, 18, 18, 14, 14,],
    "continent": ["SA", "SA", "SA", "E", "E", "E", "E"],
    "wins": [1, 5, 2, 4, 4, 1, 1],
    "runnerup": [2, 7, 5, 8, 6, 1, 1]
}

df = pd.DataFrame(data, 
        index=["Uruguay", "Brazil", "Argentina", "Germany", "Italy", "Spain", "England"],)
df


Out[160]:
continent names participated runnerup wins
Uruguay SA Uruguay 12 2 1
Brazil SA Brazil 20 7 5
Argentina SA Argentina 16 5 2
Germany E Germany 18 8 4
Italy E Italy 18 6 4
Spain E Spain 14 1 1
England E England 14 1 1

In [161]:
df.plot(kind="barh", grid=True, stacked=True, rot=30)


Out[161]:
<matplotlib.axes.AxesSubplot at 0xdfc81cc>

In [159]:
df.continent.value_counts().plot(kind="pie", label="Continents").axis("equal")


Out[159]:
(-1.0000000210975502,
 1.0000000424121425,
 -1.0253279992249265,
 1.0083560303418613)

অনেক কিছুই দেখলাম, এখন চলুন কিছু অ্যাডভান্সড কয়েরি করি।

ধরুন, আপনি চাচ্ছেন যে সমান সংখ্যকবার চ্যাম্পিয়ন হওয়া দেশগুলি একত্রিত হোক। এর জন্য আপনাকে বুঝতে হবে পিভটইং ও স্টয়াকিং। পিভট হল কোন একটি অ্যাট্রিবিউটকে ইউনিক রেখে সেই ভ্যালুর সাথেকার অন্যান্য অ্যাট্রিবিউটকে সাজান। অনেকটা সেট এর ক্রস প্রোডাক্টের মত, একটু রিয়ারেঞ্জ আরকি। মনে করুন আপনি ইনডেক্সকে ধরে পুরা টাব্লকে ঘুরালেন। stack এর মাধ্যমে টেব্লে আঁকারে সাজান হয় আর unstack এর মাধ্যমে লিস্ট আঁকারে। মনে রাখবেন, কলাম ইউনিক থাকবে, আর ইনডেক্স অনুযায়ী একত্রিত হবে। অর্থাৎ, যদি কলাম হয় names আর wins হয় ইনডেক্স, তাহলে, এমন হবে, "প্রতিটি কলাম ভ্যালু বনাম ইনডেক্সের প্রতিটি ভ্যালু, থাকলে সেই ভ্যালু, না থাকলে NaN.


In [213]:
pivoted = df.pivot(index="wins", columns="names")

In [210]:
pivoted.unstack()
# বিশাল এক ক্রস প্রোডাক্ট। NaN ভ্যালু হল খালি ডাটার জন্যে।


Out[210]:
              names      wins
participated  Argentina  1      NaN
                         2       16
                         4      NaN
                         5      NaN
              Brazil     1      NaN
                         2      NaN
                         4      NaN
                         5       20
              England    1       14
                         2      NaN
                         4      NaN
                         5      NaN
              Germany    1      NaN
                         2      NaN
                         4       18
                         5      NaN
              Italy      1      NaN
                         2      NaN
                         4       18
                         5      NaN
              Spain      1       14
                         2      NaN
                         4      NaN
                         5      NaN
              Uruguay    1       12
                         2      NaN
                         4      NaN
                         5      NaN
runnerup      Argentina  1      NaN
                         2        5
                         4      NaN
                         5      NaN
              Brazil     1      NaN
                         2      NaN
                         4      NaN
                         5        7
              England    1        1
                         2      NaN
                         4      NaN
                         5      NaN
              Germany    1      NaN
                         2      NaN
                         4        8
                         5      NaN
              Italy      1      NaN
                         2      NaN
                         4        6
                         5      NaN
              Spain      1        1
                         2      NaN
                         4      NaN
                         5      NaN
              Uruguay    1        2
                         2      NaN
                         4      NaN
                         5      NaN
Length: 56, dtype: float64

In [214]:
pivoted.stack()
# (wins, names) কে ধরে ডাটার চেহারা, unstack হয়েছে কলয়াপ্সেড। stack সমস্ত N/A কে চুপসে দিয়েছে।


Out[214]:
continent participated runnerup
wins names
1 England E 14 1
Spain E 14 1
Uruguay SA 12 2
2 Argentina SA 16 5
4 Germany E 18 8
Italy E 18 6
5 Brazil SA 20 7

ডাটাফ্রেমের শেষ এখানেই না, আরও আলোচনা হবে পরবর্তী পোস্টগুলতে। শেষ করার আগে দেখে নেই কি কি ডিসকাস হল এখানে

  • ডাটাফ্রেম কি।
  • ডাটাফ্রেম বিভিন্নভাবে সৃষ্ট হতে পারে। দ্বিমাত্রিক অ্যারে অথবা ডিকশনারির মাধ্যমে।
  • ডাটাফ্রেমের উপর আপনি বিভিন্ন ফাংশন কল এবং অপারেশান করতে পারেন।
  • ix ফাংশনের মাধ্যমে ইন্ডেক্সিং করা যায় ঠিক নামপাই অ্যারের মতই। কলাম ও ইনডেক্স দিয়ে আপনি [] এর কাজ করতে পারেন।
  • প্লটিং করা যায় ডাটাফ্রেমের মাধ্যমে। আর axis দিয়ে আপনি ড়ো/কলামকে মুখ্য ধরে নিতে পারেন।
  • সব শেষে আমরা কিছুটা দেখেছি পিভট টেব্ল। এর উপর আরও কাজ হবে। আমি কোন ভাল ডাটা (যা আমার অফিস এর না। আলস্য!!!) পাই নাই, তা পেলে আরও ভালভাবে বলা যেত।

আগামীতে আমি ডাটা কালেকশন নিয়ে কথা বলবত কিছু, আরও কিছু কথা বলব পিভট নিয়ে হয়ত। আর কথা বলব গ্রউপিং নিয়ে। তো দেখা হবে আগামীকাল।